/* Platform independent parts of DCALC - Reverse Polish Notation (RPN)
 * calculator.

    Contact info:
    bob.hepple@gmail.com
    http://bhepple.freeshell.org/dcalc/unix

    $Id: dcalc.c,v 1.13 2006/12/30 22:46:07 bhepple Exp $

    Copyright (C) 1999-2012 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

 */

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> /* for pipe() */
#include <sys/types.h> /* for wait */
#include <sys/wait.h> /* for wait */
#include <time.h> /* for localtime */
#include <float.h> /* for DBL_MAX */

extern int errno;

#ifdef HAVE_CONFIG_H /* gtk & wx */
#  include "config.h"
#endif

#include "dcalc.h"

#ifdef HAS_ALGEBRAIC_MODE
/* for yacc parser: */
char *parserPointer;
extern void yyparse();
long    xSave, ySave, zSave, tSave, uSave, lSave;  /* Shadow of Integer stack */
double  xfSave,yfSave,zfSave,tfSave,ufSave,lfSave; /* Shadow of floating point stack */
char last_eval[200];
int    algebraic_mode = 0;
#endif

long    xiReg, yiReg, ziReg, tiReg, liReg;  /* Integer stack */
long    reg[NUMREGS];   /* Integer registers */

double  xfReg,yfReg,zfReg,tfReg,lfReg; /* Floating point stack */
double regf[NUMREGS],  /* Floating point registers                  */
    pi,             /* The usual pi.                             */
    d_to_r,         /* Degrees to radians factor                 */
    r_to_d;         /* Radians to degrees factor                 */
/*                                           */
BOOLEAN entering,       /* True if we are still entering into X      */
    lift_needed,    /* True if the stack needs lifting before    */
    /* more characters are put into X            */
    invert,         /* Invert flag - causes the next operation   */
    /* to be inversed eg arcsin() instead of sin */
    degree,         /* True if numbers are in degrees; else      */
    /* radians                                   */
    was_initialised = 0;

/* To make sure program is only initialised once */
BOOLEAN
last_was_fin_key;/* True if last operation was financial      */
BOOLEAN finPayAt0 = 0;

char inbuf[45],      /* Where typed characters are put            */
    *inptr,         /* Pointer into inbuf                        */
    outbuf[45],     /* General buffer used to format output      */
    zero_string[] = "0",
    div_by_zero[] = "Division by 0!";

int decplaces,  /* number of decimal places                  */
    max_digits;     /* Maximum number of digits displayable      */

int     mode, intmode, floatmode;

typedef unsigned char byte;

#ifdef EBOOKMAN
#define DIALOG_LETTERS
#define DIALOG_NUMBERS
#else
#define DIALOG_LETTERS "(Y or N)"
#define DIALOG_NUMBERS "[0-9]"
#endif

/* function prototypes: */
static void echo_char(byte c);
static void save_x(void );
#if 0
static long round(double f);
#endif
static void process_digit(COMMAND c);
static void factors(long orig);

int 
isInverted() {
    int retval = invert;

    invert = FALSE;
    print_inv();
    clear_msg();
    return retval;
}

static void fmt_ip(char *str, int x) {
    int negative, i1,i2,i3,i4;

    if (x < 0) {
        x = x & 0x7fffffff;
        negative = 1;
    } else {
        negative = 0;
    }
    i4 = x & 0xff;
    x = x >> 8;
    i3 = x & 0xff;
    x = x >> 8;
    i2 = x & 0xff;
    x = x >> 8;
    if (negative)
        x = x | 0x80;
    i1 = x & 0xff;
    sprintf(str, "%03d.%03d.%03d.%03d", i1, i2, i3, i4);
}

void fmt_bin(char *str, long x) {
    char        inverted[50],
        reverted[50],
        *s, *t;
    BOOLEAN negative;
    
    if (x < 0) {
        negative = TRUE;
        x &= 0x7FFFFFFF;
    } else
        negative = FALSE;
    s = inverted;
    while (x != 0) {
        *(s++) = '0' + (char) (x % 2);
        x /= 2;
    }
    *s = ASCIIZ;
    if (negative) {
        while (strlen(inverted) < 31) {
            *(s++) = '0';
            *s = ASCIIZ;
        }
        *(s++) = '1';
    }
    if (s == inverted) /* x==0 */
        strcpy(str, zero_string);
    else {
        t = reverted;
        while (s != inverted)
            *(t++) = *(--s);
        *t = ASCIIZ;
        sprintf(str, "%32.32s", reverted);
    }
}

void fmt_base(char *s, long x, double xf) {
    byte        format[20];
    byte        *p;
    union       frig {
        long j;
        byte c[4];
    } frigger;
    int         i, leading_zero;
    
    if (mode == PROG) {
        switch (intmode) {
        case ASCIIM:
            frigger.j = x;
            for (i = 0; i < 4; i++) {
#if REVERSE_BYTES
                format[3 - i] = frigger.c[i];
#else
                format[i] = frigger.c[i];
#endif
            }
            format[4] = 0;
#if PCSPECIFIC
            p = format;
            for (i = 0; i < 4; i++, p++)
                if (*p == 0)
                    *s++ = ' ';
                else
                    *s++ = *p;
            *s = 0;
#else
            /* Convert to canonical form */
            p = format;
            leading_zero = 1;
            for (i = 0; i < 4; i++, p++) {
                if ((i < 3) && (*p == 0) && leading_zero)
                    continue;
                leading_zero = 0;
                if (*p & 0x80) {
                    strcpy(s, "a-");
                    s += 2;
                    *p &= 0x7f;
                }
                switch (*p) {
                case 0:
                    strcpy(s, "^spc");
                    s += 4;
                    break;
                case 7:
                    strcpy(s, "bell");
                    s += 4;
                    break;
                case 8:
                    strcpy(s, "bs");
                    s += 2;
                    break;
                case 9:
                    strcpy(s, "tab");
                    s += 3;
                    break;
                case 10:
                    strcpy(s, "lf");
                    s += 2;
                    break;
                case 13:
                    strcpy(s, "cr");
                    s += 2;
                    break;
                case 27:
                    strcpy(s, "esc");
                    s += 3;
                    break;
                case 32:
                    strcpy(s, "spc");
                    s += 3;
                    break;
                case 0x7F:
                    strcpy(s, "del");
                    s += 3;
                    break;
                default:
                    if (*p < 32) {
                        *s++ = '^';
                        *s++ = *p+0x60;
                    } else
                        *s++ = *p;
                }
                *s++ = ' ';
            }
            *s = 0;
#endif
            break;
        
        case BIN:
            fmt_bin(s, x);
            break;
        
        case IP:
            fmt_ip(s, x);
            break;

        case OCT:
            if (x == 0L)
                strcpy(s, zero_string);
            else
                sprintf(s, "%32.1lo", x);
            break;
        case DEC:
            if (x == 0L)
                strcpy(s, zero_string);
            else
                sprintf(s, "%32.1ld", x);
            break;
        case HEX:
            if (x == 0L)
                strcpy(s, zero_string);
            else
                sprintf(s, "%32.1lx", x);
            break;
        }
    } else {
        if (xf == 0.0) {
            strcpy(s, "0.0");
            return;
        }

        if (floatmode == SCI) {
            sprintf(format, "%%32.%dE", decplaces);
            sprintf(s, format, xf);
        } else if (floatmode == ENG) {
            double mant;
            int isNeg = 0, exp;
            char *e, outb[80];

            if (xf < 0.0)
                isNeg = 1;
            mant = fabs(xf);
            exp = log10(mant);
            exp = exp / 3;
            exp = exp * 3;
            mant = mant * pow(0.1, (double) exp);
            while (mant < 0.1) {
                mant = mant * 1000.0;
                exp = exp - 3;
            }
            if (isNeg)
                mant = -mant;
            sprintf(outb, "%32.2E", pow(10.0, exp));
            e = strchr(outb, 'E');
            if (e) {
                sprintf(format, "%%32.%df%%s", decplaces);
                sprintf(s, format, mant, e);
            } else {
                sprintf(format, "%%32.%dg", decplaces);
                sprintf(s, format, xf);
            }
        } else { /* FIX */
            double lowerLimit = pow(10.0, -decplaces);
#ifdef EBOOKMAN
            double upperlimit = 1.0e15 / pow(10.0, decplaces);
#else
            double upperlimit = 1.0e25 / pow(10.0, decplaces);
#endif
            if ((fabs(xf) > upperlimit) || (fabs(xf) < lowerLimit)) {
                sprintf(format, "%%32.%dE", decplaces);
            } else {
                sprintf(format, "%%32.%df", decplaces);
            }
            sprintf(s, format, xf);
        }
    }
}

void prep_for_output(char *string) {
    /* Put suitable separators in string */
    char        *s, *d, sep = 0, *ft, rhs[45], lhs[45], scratch[45], prefix[3];
    int         len, interval = 0, count, negative;
    
    prefix[0] = 0;
    if (mode == PROG) {
        switch (intmode) {
        case ASCIIM:
            return; /* Nasty to return here - its easy to miss it when reading! */
        case IP:
            return; /* all done by fmt_ip */
        case BIN:
            sep = SPACE;
            interval = 8;
            break;
        case OCT:
            sep = SPACE;
            interval = 3;
            strcpy(prefix, "0");
            break;
        case DEC:
            sep = COMMA;
            interval = 3;
            break;
        case HEX:
            sep = SPACE;
            interval = 2;
            strcpy(prefix, "0x");
            break;
        }
    } else { /* FIX or ENG */
        sep = COMMA;
        interval = 3;
    }
    
    /* Find first non-blank ... */
    s = string;
    while (*s && isspace(*s)) s++;
    if (*s == '-') {
        negative = TRUE;
        *s++ = SPACE;
    } else
        negative = FALSE;
    
    /* move rhs to safe keeping - fraction or exponent part */
    ft = NULL;
    *rhs = 0;
    if ((mode != PROG) && ((ft = strchr(s, '.')) ||
                           (ft = strchr(s, 'E')) ||
                           (ft = strchr(s, 'e')))) {
        strcpy(rhs, ft);
        *ft = 0;
    }
    
    /* add separators to the integer part ... */
    strcpy(lhs, s);
    len = strlen(s);
    if (len > interval) {
        count = 0;
        d = scratch;
        s = s + len - 1;
        while (--len >= 0) {
            if (count++ == interval) {
                *d++ = sep;
                count = 1;
            }
            *d++ = *s--;
        }
        *d = 0;
        s = scratch + strlen(scratch) - 1;
        d = lhs;
        while (s >= scratch)
            *d++ = *s--;
        *d = 0;
    }
    
    /* Now reassemble ... */
    d = string;
    if (negative) {
        *d++='-';
    }
    *d = 0;
    strcpy(d, prefix);
    strcat(d, lhs);
    strcat(d, rhs);
}

long 
asc2int(char *s) {
    union       frig {
        long j;
        byte c[4];
    } frigger;
    int         i;
    
    for (i = 0; i < 4; i++)
#if REVERSE_BYTES
        frigger.c[3 - i] = *s++;
#else
    frigger.c[i] = *s++;
#endif
    return(frigger.j);
}

#if 0
long 
bin2int(char *s) {
    char        *t;
    long        accum;
    
    accum = 0;
    t = s;
    while (*t)
        accum = 2 * accum + (*(t++) - '0');
    return(accum);
}
#endif

long 
ip2int(char *s) {
    int index, i[4], leftBitSet = 0;
    long retval;

    if (!s || !*s)
        return 0;

    for (index = 0; index < 4; index++) {
        char *dot = strchr(s, '.');
        i[index] = 0;
        if (dot)
            *dot = 0;
        if (strlen(s)) {
            i[index] = atoi(s);
            i[index] &= 0xff;
            s += strlen(s);
        }
        if (dot) {
            *dot = '.';
            s = dot + 1;
        }
    }
    if (i[0] > 0x7f) {
        leftBitSet = 1;
        i[0] &= 0x7f;
    }
    retval = i[0];
    retval = (retval << 8) | i[1];
    retval = (retval << 8) | i[2];
    retval = (retval << 8) | i[3];
    if (leftBitSet)
        retval |= 0x80000000;
    return retval;
}

/* Note that strtol appears to have a bug - it cannot accept negative
 * hex, octal or binary numbers e.g. 0xffffffff, which is -1, hence
 * the special processing */
void
parseBuffer(char *buf, long *longResult, double *doubleResult) {
    if (mode == PROG) {
        long temp = 0;
        int isNeg = 0;
        char *s, *d;

        /* remove leading 0's unless all are 0 */
        d = buf;
        s = buf;
        while (*s == '0')
            s++;
        if (s != d) {
            while (*s) 
                *d++ = *s++;
            *d = 0;
        }

        switch (intmode) {
        case ASCIIM:
            temp = asc2int(buf);
            break;
        case IP:
            temp = ip2int(buf);
            break;
        case BIN:
            if (strlen(buf) == 32 && *buf == '1') {
                d = buf; s= d + 1; while (*s) *d++=*s++; *d=0;
                isNeg = 1;
            }
            temp = strtol(buf, NULL, 2);
            break;
        case OCT:
            if (strlen(buf) == 11) {
                if (*buf == '2') {
                    d = buf; s= d + 1; while (*s) *d++=*s++; *d=0;
                    isNeg = 1;
                }
                if (*buf == '3') {
                    *buf = '1';
                    isNeg = 1;
                }
            }
            temp = strtol(buf, NULL, 8);
            break;
        case DEC:
            temp = strtol(buf, NULL, 10);
            break;
        case HEX:
            if (strlen(buf) == 8) {
                char b[2];
                int leftDigit;
                b[0] = *buf;
                b[1] = 0;
                leftDigit = strtol(b, NULL, 16);
                if (leftDigit & 8) {
                    *buf = (leftDigit & 7) + '0';
                    isNeg = 1;
                }
            }
            temp = strtol(buf, NULL, 16);
            break;
        }
        *longResult = isNeg? temp|0x80000000: temp;
    } else
        *doubleResult = strtod(buf, NULL);
}

void stop_entering() {

    if (entering) {
        entering = FALSE;
        lift_needed = TRUE;
        parseBuffer(inbuf, &xiReg, &xfReg);
        inptr = inbuf;
        *inptr = 0;
    }
}

void push(long inx) {
    stop_entering();
    tiReg = ziReg;     
    ziReg = yiReg;     
    yiReg = xiReg;     
    xiReg = inx;   
    lift_needed = TRUE;
}

void pushf(double inxf) {
    stop_entering();
    tfReg = zfReg;
    zfReg = yfReg;
    yfReg = xfReg;
    xfReg = inxf;
    lift_needed = TRUE;
}

long pop() {
    long retval;
    stop_entering();
    retval = xiReg;    
    xiReg = yiReg;        
    yiReg = ziReg;        
    ziReg = tiReg;
    return retval;
}

double popf() {
    double retval;
    stop_entering();
    retval = xfReg;    
    xfReg = yfReg;        
    yfReg = zfReg;        
    zfReg = tfReg;
    return retval;
}

int
liftStack() {
    long        temp;
    double      tempf;

    if (lift_needed) {
        if (mode == PROG) {
            temp = xiReg;
            push(temp); 
        } else {
            tempf = xfReg;
            pushf(tempf);
        }
        dispnums();
        lift_needed = FALSE;
        return 1;
    } else
        return 0;
}

static void echo_char(byte c) {
    /* echo 'c' back to the screen and log in the input buffer */
    char        *p;
    
    if ((c == BACKSPACE) && (mode != PROG || intmode != ASCIIM)) {
        if (entering && (inptr > inbuf)) {
            inptr--;
            *inptr = ASCIIZ;
        } else {
            if (mode == PROG)
                xiReg = 0;
            else
                xfReg = 0.0;
            inptr = inbuf;
            *inptr = ASCIIZ;
            entering = TRUE;
            dispnums();
        }

    } else /* Neither backspace or ASCII mode */ {
        if (!entering) {
            liftStack();
            entering = TRUE;
            for (inptr = inbuf; inptr < inbuf + 4; inptr++)
                *inptr = ASCIIZ;
            inptr = inbuf;
        }
        
        if (inptr < inbuf + max_digits)
            if (mode == PROG && intmode == ASCIIM) {
                for (p = inbuf; p < inbuf + 3; p++)
                    *p = *(p+1);
                inbuf[3] = c;
                inptr++;
            } else {
                *inptr++ = c;
                *inptr = ASCIIZ;
            }
        else {
            put_a_char(BELL);
            msg("Too many digits!");
        }
    }
    
    if (mode == PROG && intmode == ASCIIM) {
        xiReg = asc2int(inbuf);
        fmt_base(outbuf, xiReg, xfReg);
    } else {
        strcpy(outbuf, inbuf);
    }
    prep_for_output(outbuf);
    print_x(outbuf);
}

void display() {
    prinbase();
    print_deg();
    dispnums();
    dispregs();
    invert = FALSE;
    print_inv();
}

static void save_x() {
    stop_entering();
    liReg = xiReg;
    lfReg = xfReg;
}

#if 0
static long round(double f) {
    BOOLEAN     negative;
    long        ret;
    
    negative = FALSE;
    if (f < 0.0) {
        negative = TRUE;
        f = -f;
    }
    ret = floor(f + 0.5);
    if (negative)
        ret = -ret;
    return(ret);
}
#endif

int
exec_asciim() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != ASCIIM) {
        long tempx;
        tempx = pop();
        push(tempx);
        mode = PROG;
        intmode = ASCIIM;
        msg("ASCII mode");
        os_raw_mode(1);
        max_digits = 4;

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
static
void change_floatmode(COMMAND c) {
    last_was_fin_key = 0;
    if (c != floatmode) {
        char buf[80];

        floatmode = (c == SCIFORMAT? SCI: c);
        stop_entering();
        os_raw_mode(0);
        max_digits = 20;
        sprintf(buf, "%s notation", 
                (c==ENG)? "Engineering": 
                (c==SCIFORMAT)? "Scientific":
                "Fixed point");
        msg(buf);

        dispnums();
        dispregs();
        prinbase();
    }
}

int
exec_eng() { 
    isInverted();
    last_was_fin_key = 0; 
    change_floatmode(ENG); 
    return 0;
}

int
exec_fix() { 
    isInverted();
    last_was_fin_key = 0; 
    change_floatmode(FIX); 
    return 0;
}

int
exec_sciformat() { 
    isInverted();
    last_was_fin_key = 0; 
    change_floatmode(SCIFORMAT); 
    return 0;
}
        
int
exec_places() {
    long tempx;

    isInverted();
    last_was_fin_key = 0;
    if (((tempx = places("Enter number of decimal places")) >= 0) &&
        (tempx != decplaces)) {
        char buf[80];

        stop_entering();
        decplaces = tempx;
        os_raw_mode(0);
        max_digits = 20;
        sprintf(buf, "Places set to %d", decplaces);
        msg(buf);

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
int
exec_ip() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != IP) {
        long tempx;
        tempx = pop();
        push(tempx);
        mode = PROG;
        os_raw_mode(0);
        max_digits = 15;
        intmode = IP;
        msg("IP address mode");

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
int
exec_bin() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != BIN) {
        long tempx;
        tempx = pop();
        push(tempx);
        mode = PROG;
        os_raw_mode(0);
        max_digits = 32;
        intmode = BIN;
        msg("Binary mode");

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
int
exec_oct() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != OCT) {
        long tempx;
        tempx = pop();
        push(tempx);
        intmode = OCT;
        mode = PROG;
        os_raw_mode(0);
        max_digits = 11;
        msg("Octal mode");

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
int
exec_dec() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != DEC) {
        long tempx;
        tempx = pop();
        push(tempx);
        mode = PROG;
        intmode = DEC;
        os_raw_mode(0);
        max_digits = 10;
        msg("Decimal mode");

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}
        
int
exec_hex() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG || intmode != HEX) {
        long tempx;
        tempx = pop();
        push(tempx);
        mode = PROG;
        intmode = HEX;
        os_raw_mode(0);
        max_digits = 8;
        msg("Hexadecimal mode");

        dispnums();
        dispregs();
        prinbase();
    }
    return 0;
}

static  
void change_fin_sci_stat(COMMAND c) {
    last_was_fin_key = 0;
    if (mode != c) {
        char buf[80];
        mode = c;
        stop_entering();
        os_raw_mode(0);
        max_digits = 20;
        sprintf(buf, "%s mode", (c==FIN)? "Financial": (c==SCI)? "Scientific": "Statistics");
        if (c == FIN) {
            if (floatmode != FIX)
                exec_fix();
            if (decplaces != 2)
                decplaces = 2;
            dispnums();
        }
        msg(buf);

        dispnums();
        dispregs();
        prinbase();
    }
}

int
exec_fin() { 
    isInverted();
    change_fin_sci_stat(FIN); 
    return 0;
}

int
exec_sci() { 
    isInverted();
    change_fin_sci_stat(SCI); 
    return 0;
}

int
exec_stat() { 
    isInverted();
    change_fin_sci_stat(STAT); 
    return 0;
}

int
exec_prog() {
    isInverted();
    last_was_fin_key = 0;
    if (mode != PROG) {
        mode = PROG;
        stop_entering();
        os_raw_mode(0);
        max_digits = 10;
        msg("Programmers mode");
    }

    dispnums();
    dispregs();
    prinbase();
    return 0;
}

static void addPrime(long cand, long *primes, int *primeMultiplier, int *numPrimes) {
    if (*numPrimes == 0 ) {
        *numPrimes = 1;
        primes[0] = cand;
        primeMultiplier[0] = 1;
    } else {
        if (primes[*numPrimes - 1] == cand) {
            primeMultiplier[*numPrimes - 1] = primeMultiplier[*numPrimes - 1] + 1;
        } else {
            if (*numPrimes == KMaxPrimes) {
                msg("Too many primes");
                return;
            }
            primes[*numPrimes] = cand;
            primeMultiplier[*numPrimes] = 1;
            (*numPrimes)++;
        }
    }
}

static
void factors(long orig) {
    long        target;
    int         i, isNeg = 0;
    char        msgbuf[100];
    long        cand, rem, term;
    long        primes[KMaxPrimes];
    int         primeMultiplier[KMaxPrimes], numPrimes;

    if (orig < 0) {
        isNeg = 1;
        orig = -orig;
    }

    if (orig < 2) {
        msg("No prime factors");
        return;
    }

    target = orig;
    term = sqrt((double) target) + 1;
    numPrimes = 0;
    for (i = 0; i < KMaxPrimes; i++) {
        primes[i] = 0;
        primeMultiplier[i] = 0;
    }

    // check for 2's
    cand = 2;
    while (cand < term) {
        rem = target / cand;
        if (rem * cand == target) {
            target = rem;
            term = sqrt(target) + 1;
            addPrime(cand, primes, primeMultiplier, &numPrimes);
        } else
            break;
    }

    // check odd numbers
    cand = 3;
    while (cand < term) {
        rem = target / cand;
        if (rem * cand == target) {
            target = rem;
            term = sqrt(target)+1;
            addPrime(cand, primes, primeMultiplier, &numPrimes);
        } else
            cand += 2;
    }
    if (target > 1)
        addPrime(target, primes, primeMultiplier, &numPrimes);

    if (numPrimes == 1 && primeMultiplier[0] == 1) {
        sprintf(msgbuf, "%ld is prime", orig);
    } else {
#ifdef EBOOKMAN
        msgbuf[0] = 0;
#else
        sprintf(msgbuf,"%ld=", orig);
#endif
        for (i=0, target = 1; i < numPrimes; i++) {
            char *m = msgbuf;

            m += strlen(msgbuf);
#ifdef DEBUG
            {
                int j;
                for (j = 0; j < primeMultiplier[i]; j++)
                    target *= primes[i];
            }
#endif
            sprintf(m, "%ld", primes[i]);
            if (primeMultiplier[i] > 1) {
                m += strlen(m);
                sprintf(m, "^%d", primeMultiplier[i]);
            }
            if (i < numPrimes - 1) {
                strcat(m, "�");
            }
        }
    }
    msg(msgbuf);

#ifdef DEBUG
    if (target != orig) { // ERRORCHECKING IN BETA ONLY!!!
        msg("Error!");
    }
#endif
}

static int power(int x, int n) {
    int i, p;
    switch (x) {
    case 0:
        return 0;
    case 1:
        return 1;
    default:
        switch (n) {
        case 0:
            return 1;
        case 1:
            return x;
        default:
            if (n < 0 || n > 32) {
                msg("Overflow");
                return 0;
            }
            p = 1;
            for (i = 1; i <= n; ++i)
                p *= x;
            return p;
        }
    }
}

/* static void arith_int(COMMAND c) { */
int
exec_chs() {    
    long        tempx;
    double      tempxf;
    char        *c_ptr, c_buf[45];

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG) {
        if (entering) {
            c_ptr = inbuf;
            strcpy(c_buf, c_ptr);
            *c_ptr = ASCIIZ;
            c_ptr = c_buf;
            if (*c_ptr == '-')
                c_ptr++;
            else
                strcat(inbuf, "-");
            strcat(inbuf, c_ptr);
            inptr = &inbuf[strlen(inbuf)];
        } else {
            tempx = pop(); 
            push(-tempx); 
        }
    } else {
        if (entering) {
            if ((c_ptr = strchr(inbuf, 'E')))
                c_ptr++;
            else
                c_ptr = inbuf;
            strcpy(c_buf, c_ptr);
            *c_ptr = ASCIIZ;
            c_ptr = c_buf;
            if (*c_ptr == '-')
                c_ptr++;
            else
                strcat(inbuf, "-");
            strcat(inbuf, c_ptr);
            inptr = &inbuf[strlen(inbuf)];
        } else {
            tempxf = popf();
            pushf(-tempxf);
        }
    }
    dispnums();
    return 0;
}

/* operations on X only */
int
exec_not() { 
    long        tempx;
    double      tempxf;

    isInverted();
    save_x();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = pop();
        tempx = ~tempx;
        push(tempx);
    } else {
        tempxf = popf();
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

int
exec_shiftl() {
    long        tempx;
    double      tempxf;

    if (isInverted())
        return exec_shiftr();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempx = tempx << 1;
        push(tempx);
    } else {
        tempxf = popf();
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

int
exec_shiftr() {
    long        tempx;
    double      tempxf;

    if (isInverted())
        return exec_shiftl();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempx = tempx >> 1;
        push(tempx);
    } else {
        tempxf = popf();
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

int
exec_primef() {
    long        tempx;
    double      tempxf;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        factors(tempx);
        push(tempx);
    } else {
        tempxf = popf();
        pushf(tempxf);
    }
    dispnums();
    return 0;
}
        
int
exec_sqr() {
    long        tempx;
    double      tempxf;

    if (isInverted())
        return exec_root();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempx = tempx * tempx;
        push(tempx);
    } else {
        tempxf = popf();
        tempxf *= tempxf;
        pushf(tempxf);
    }
    dispnums();
    return 0;
}
                
int
exec_root() {
    double      tempxf;

    if (isInverted())
        return exec_sqr();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempxf = pop();
    } else
        tempxf = popf();

    tempxf = sqrt(tempxf);
    if (mode == PROG)
        push((long)(tempxf+0.5));
    else
        pushf(tempxf);

    dispnums();
    return 0;
}
        
int
exec_cube() {
    long        tempx;
    double      tempxf;

    if (isInverted())
        return exec_croot();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempx = tempx * tempx * tempx;
        push(tempx);
    } else {
        tempxf = popf();
        tempxf = tempxf * tempxf * tempxf;
        pushf(tempxf);
    }
    dispnums();
    return 0;
}
                
int
exec_croot() {
    double      tempxf;

    if (isInverted())
        return exec_cube();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempxf = pop();
    } else
        tempxf = popf();

    tempxf = pow(tempxf, 1.0 / 3.0);
    if (mode == PROG)
        push((long)(tempxf+0.5));
    else
        pushf(tempxf);

    dispnums();
    return 0;
}
        
int
exec_reci() {
    long        tempx;
    double      tempxf;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempx = 0;
        push(tempx);
    } else {
        tempxf = popf();
        if (tempxf == 0.0)
            msg(div_by_zero);
        else {
            tempxf = 1.0 / tempxf;
        }
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* X & Y operations */
int
exec_plus() {
    long        tempx, tempy;
    double      tempxf, tempyf;

    if (isInverted())
        return exec_minus();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx = tempx + tempy;
        push(tempx);
    } else {
        tempxf = popf();
        tempyf = popf();
        tempxf = tempxf + tempyf;
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* case '-': */
int
exec_minus() {
    long        tempx, tempy;
    double      tempxf, tempyf;

    if (isInverted())
        return exec_plus();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx = tempy - tempx;
        push(tempx);
    } else {
        tempxf = popf();
        tempyf = popf();
        tempxf = tempyf - tempxf;
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* case '*': */
int
exec_mult() {
    long        tempx, tempy;
    double      tempxf, tempyf;

    if (isInverted())
        return exec_divide();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx = tempx * tempy;
        push(tempx);
    } else {
        tempxf = popf();
        tempyf = popf();
        tempxf = tempxf * tempyf;
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* case '/': */
int
exec_divide() {
    long        tempx, tempy;
    double      tempxf, tempyf;

    if (isInverted())
        return exec_mult();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        if (tempx == 0) {
            push(tempy);
            msg(div_by_zero);
        } else {
            if (tempx != 0)
                tempx = tempy / tempx;
            else
                tempx = 0;
        }
        push(tempx);
    } else {
        tempxf = popf();
        tempyf = popf();
        if (tempxf == 0.0) {
            pushf(tempyf);
            msg(div_by_zero);
        } else {
            tempxf = tempyf / tempxf;
        }
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* case '^': */
int
exec_xor() {
    long        tempx, tempy;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx ^= tempy;
        push(tempx);
    } else {
    }
    dispnums();
    return 0;
}

/* case '|': */
int
exec_or() {
    long        tempx, tempy;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx |= tempy;
        push(tempx);
    } else {
    }
    dispnums();
    return 0;
}

/* case '&': */
int
exec_and() {
    long        tempx, tempy;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempx &= tempy;
        push(tempx);
    } else {
    }
    dispnums();
    return 0;
}

/* case '<': */
int
exec_shiftyl() {
    long        tempx, tempy;

    if (isInverted())
        return exec_shiftyr();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempy <<= tempx;
        tempx = tempy;
        push(tempx);
    } else {
    }
    dispnums();
    return 0;
}

/* case '>': */
int
exec_shiftyr() {
    long        tempx, tempy;

    if (isInverted())
        return exec_shiftyl();

    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        tempy >>= tempx;
        tempx = tempy;
        push(tempx);
    } else {
    }
    dispnums();
    return 0;
}

int
exec_modulus() {
    long        tempx, tempy;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        if (tempx == 0) {
            push(tempx);
            msg(div_by_zero);
        } else {
            tempy = pop(); 
            tempy %= tempx;
            tempx = tempy;
            push(tempx);
        }
    } else {
    }
    dispnums();
    return 0;
}

int
exec_ytox() {
    long        tempx, tempy;
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
        tempx = pop();
        tempy = pop(); 
        if (tempx < 0) {
            msg("X cannot be < 0");
            push(tempy);
        } else
            tempx = power(tempy, tempx);
        push(tempx);
    } else {
        tempxf = popf();
        tempyf = popf();
        tempxf = pow(tempyf, tempxf);
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

int
exec_percent() {
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
    } else {
        tempxf = popf();
        tempyf = popf();
        pushf(tempyf); /* push y back on stack for future ops */
        if (isInverted()) {
            if (tempxf == 0.0) {
                msg(div_by_zero);
                pushf(tempxf);
            } else {
                tempxf = tempyf * 100.0 / tempxf;
            }
        } else {
            tempxf = tempxf * tempyf / 100.0;
        }
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

int
exec_percentch() {
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    if (mode == PROG) {
    } else {
        tempxf = popf();
        tempyf = popf();
        pushf(tempyf); /* push y back on stack for future ops */
        if (isInverted()) {
            if (tempxf == 0.0) {
                msg(div_by_zero);
                pushf(tempxf);
            } else {
                tempxf = tempyf * (1.0 + tempxf / 100.0);
            }
        } else {
            tempxf = 100.0 * (tempxf - tempyf) / tempyf;
        }
        pushf(tempxf);
    }
    dispnums();
    return 0;
}

/* static void scientific(COMMAND c) { */
    
int
exec_sin() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        tempxf = asin(tempxf);
        if (degree)
            tempxf *= r_to_d;
    } else {
        if (degree)
            tempxf = sin(d_to_r * tempxf);
        else
            tempxf = sin(tempxf);
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_asin() {
    invert = TRUE;
    return(exec_sin());
}

int
exec_cos() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        tempxf = acos(tempxf);
        if (degree)
            tempxf *= r_to_d;
    } else {
        if (degree)
            tempxf = cos(d_to_r * tempxf);
        else
            tempxf = cos(tempxf);
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_acos() {
    invert = TRUE;
    return(exec_cos());
}
        
int
exec_tan() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        tempxf = atan(tempxf);
        if (degree)
            tempxf *= r_to_d;
    } else {
        if (degree)
            tempxf = tan(d_to_r * tempxf);
        else
            tempxf = tan(tempxf);
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_atan() {
    invert = TRUE;
    return(exec_tan());
}
        
int
exec_sinh() {
    double      tempxf;
    if (mode == PROG)
        return 9;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        tempxf = log(tempxf+sqrt(tempxf*tempxf + 1));
    } else {
        tempxf = 0.5*(exp(tempxf) - exp(-tempxf));
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_asinh() {
    invert = TRUE;
    return(exec_sinh());
}
        
int
exec_cosh() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        if (tempxf < 1) {
            msg("Illegal value");
            pushf(tempxf);
        } else {
            tempxf = log(tempxf+sqrt(tempxf*tempxf - 1));
        }
    } else {
        tempxf = 0.5*(exp(tempxf) + exp(-tempxf));
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_acosh() {
    invert = TRUE;
    return(exec_cosh());
}
        
int
exec_tanh() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) {
        if (tempxf == 1) {
            msg("Illegal value");
            pushf(tempxf);
        } else {
            tempxf = 0.5*log((tempxf+1)/(1-tempxf));
        }
    } else {
        double p;
        p = exp(2*tempxf);
        tempxf = (p-1)/(p+1);
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_atanh() {
    invert = ~invert;
    return(exec_tanh());
}
        
int
exec_etox() {
    if (mode == PROG)
        return 0;

    if (isInverted())
        return exec_loge();

    last_was_fin_key = 0;
    save_x();
    pushf(exp(popf()));
    dispnums();
    return 0;
}
        
int
exec_loge() {
    if (mode == PROG)
        return 0;

    if (isInverted())
        return exec_etox();

    last_was_fin_key = 0;
    save_x();
    pushf(log(popf()));
    dispnums();
    return 0;
}

int
exec_10tox() {
    if (mode == PROG)
        return 0;

    if (isInverted())
        return exec_log10();

    last_was_fin_key = 0;
    save_x();
    pushf(pow(10.0, popf()));
    dispnums();
    return 0;
}

int
exec_log10() {
    if (mode == PROG)
        return 0;

    if (isInverted())
        return exec_10tox();

    last_was_fin_key = 0;
    save_x();
    pushf(log10(popf()));
    dispnums();
    return 0;
}
        
int
exec_frc() {
    double      tempxf;
    long tempy = 0;

    isInverted();
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (tempxf < 0.0) {
        tempy = 1;
        tempxf = -tempxf;
    }
    tempxf = tempxf - floor(tempxf);
    if (tempy)
        tempxf = -tempxf;
    pushf(tempxf);
    dispnums();
    return 0;
}
        
int
exec_int() {
    double      tempxf;
    long tempy = 0;

    isInverted();
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (tempxf < 0.0) {
        tempy = 1;
        tempxf = -tempxf;
    }
    tempxf = (double) floor(tempxf);
    if (tempy)
        tempxf = -tempxf;
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_hms() { 
    double      tempxf;
    int sign = 0;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (tempxf < 0.0) {
        sign = 1;
        tempxf = -tempxf;
    }

    if (isInverted()) { /* convert h.mmss to hours */
        long h, m, s;

        h = tempxf;
        tempxf = (tempxf - h) * 100 + 1E-10;
        m = tempxf;
        tempxf = (tempxf - m) * 100 + 1E-10;
        s = tempxf;
        tempxf = h + m / 60.0 + s / 3600.0;
        msg("HH.MMSS -> hours");
    } else { /* convert hours to h.mmss */
        long h, m, s;

        h = tempxf;
        tempxf = (tempxf - h) * 60  + 1E-10;
        m = tempxf;
        s = (tempxf - m) * 60 + 1E-10;
        tempxf = h + m / 100.0 + s / 10000.0;
        if (decplaces < 4)
            decplaces = 4;
        if (floatmode != FIX)
            exec_fix();
        msg("hours -> HH.MMSS");
    }
    if (sign)
        tempxf = -tempxf;
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_dtor() {
    double      tempxf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    if (isInverted()) { 
        tempxf *= r_to_d;
        msg("radians to degrees");
    } else {
        tempxf *= d_to_r;
        msg("degrees to radians");
    }
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_rtod() {
    invert = ~invert;
    return(exec_dtor());
}
                
int
exec_rtop() {
    double      tempxf, tempyf;
    if (mode == PROG)
        return 0;

    last_was_fin_key = 0;
    save_x();
    tempxf = popf();

    tempyf = popf();
    if (isInverted()) { /*  convert (r, theta) to (x, y) */
        double theta, r;

        theta = tempyf;
        if (degree)
            theta = d_to_r * theta;

        r = tempxf;
        tempyf = r * sin(theta);
        tempxf = r * cos(theta);
        msg("polar to rectangular");
    } else { /*  convert (x, y) to (r, theta) */
        double theta, r;

        r = sqrt(tempxf * tempxf + tempyf * tempyf);
        theta = atan2(tempyf, tempxf);
        if (degree)
            theta = r_to_d * theta;
        tempyf = theta;
        tempxf = r;
        msg("rectangular to polar");
    }
    pushf(tempyf);
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_ptor() {
    invert = ~invert;
    return(exec_rtop());
}
        
static double fact(double from, double to) {
    double total, n, m;

    n = floor(from);
    m = floor(to);
    if (n <= 0 || m <= 0)
        return(1.0);

    total = 1.0;
    while (n > m) {
        total = total * n;
        n = n - 1;
    }
    return total;
}

/* static void summation(COMMAND c) { */
    
int
exec_fact() {
    double      tempxf;

    if (mode == PROG)
        return 0;

    isInverted();
    last_was_fin_key = 0;
    save_x();
    tempxf = popf();
    if (tempxf < 0) {
        msg("Illegal value");
        pushf(tempxf);
    } else {
        pushf(fact(tempxf, 1));
        dispnums();
            
    }
    return 0;
}

int
exec_sum() {
    double      tempxf, tempyf;

    if (isInverted())
        return exec_sumr();

    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    save_x();
    tempxf = popf();
    tempyf = popf();
    pushf(tempyf);
    pushf(tempxf);
    regf[STAT_NUM_REG]++;
    regf[STAT_SUMX_REG] += tempxf;
    regf[STAT_SUMX2_REG] += tempxf * tempxf;
    regf[STAT_SUMY_REG] += tempyf;
    regf[STAT_SUMY2_REG] += tempyf * tempyf;
    regf[STAT_SUMXY_REG] += tempxf * tempyf;
    pushf(regf[STAT_NUM_REG]);
    dispreg(STAT_NUM_REG);
    dispreg(STAT_SUMX_REG);
    dispreg(STAT_SUMY_REG);
    dispreg(STAT_SUMX2_REG);
    dispreg(STAT_SUMY2_REG);
    dispreg(STAT_SUMXY_REG);
    dispnums();
    msg("X & Y accumulated");
    return 0;
}
        
int
exec_sumr() {
    double      tempxf, tempyf;

    if (isInverted())
        return exec_sum();

    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    if (regf[STAT_NUM_REG] > 0) {
        save_x();
        tempxf = popf();
        tempyf = popf();
        pushf(tempyf);
        pushf(tempxf);
        regf[STAT_NUM_REG]--;
        regf[STAT_SUMX_REG] -= tempxf;
        regf[STAT_SUMX2_REG] -= tempxf * tempxf;
        regf[STAT_SUMY_REG] -= tempyf;
        regf[STAT_SUMY2_REG] -= tempyf * tempyf;
        regf[STAT_SUMXY_REG] -= tempxf * tempyf;
        pushf(regf[STAT_NUM_REG]);
        dispreg(STAT_NUM_REG);
        dispreg(STAT_SUMX_REG);
        dispreg(STAT_SUMY_REG);
        dispreg(STAT_SUMX2_REG);
        dispreg(STAT_SUMY2_REG);
        dispreg(STAT_SUMXY_REG);
        dispnums();
        msg("X & Y decremented");
    }
    return 0;
}
        
int
exec_sum0() {
    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    save_x();
    regf[STAT_NUM_REG] = 0.0;
    regf[STAT_SUMX_REG]  = 0.0;
    regf[STAT_SUMX2_REG]  = 0.0;
    regf[STAT_SUMY_REG]  = 0.0;
    regf[STAT_SUMY2_REG]  = 0.0;
    regf[STAT_SUMXY_REG]  = 0.0;
    dispreg(STAT_NUM_REG);
    dispreg(STAT_SUMX_REG);
    dispreg(STAT_SUMY_REG);
    dispreg(STAT_SUMX2_REG);
    dispreg(STAT_SUMY2_REG);
    dispreg(STAT_SUMXY_REG);
    dispnums();
    msg("Accumulator registers cleared");
    return 0;
}
        
int
exec_nstat() {
    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    pushf(regf[STAT_NUM_REG]);
    msg("Number of entries");
    dispnums();
    return 0;
}
        
int
exec_means() { /* mean of x & y */
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    if (regf[STAT_NUM_REG] == 0.0)
        msg("Nothing accumulated yet!");
    else {
        tempyf = regf[STAT_SUMY_REG] / regf[STAT_NUM_REG];
        pushf(tempyf);
        tempxf = regf[STAT_SUMX_REG] / regf[STAT_NUM_REG];
        pushf(tempxf);
        dispnums();
        msg("Mean of X's and of Y's");
    }
    return 0;
}
        
int
exec_mean() {
    double      tempxf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    if (regf[STAT_NUM_REG] == 0.0)
        msg(div_by_zero);
    else {
        tempxf = regf[STAT_SUMX_REG] / regf[STAT_NUM_REG];
        pushf(tempxf);
        dispnums();
        msg("Mean of X's");
    }
    return 0;
}
        
int
exec_meany() {
    double tempyf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    if (regf[STAT_NUM_REG] == 0.0)
        msg(div_by_zero);
    else {
        tempyf = regf[STAT_SUMY_REG] / regf[STAT_NUM_REG];
        pushf(tempyf);
        dispnums();
        msg("Mean of Y's");
    }
    return 0;
}
        
int
exec_s_dev() {
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;

    /*  s.dev = sqrt((sum:(x^2) - (sum:(x))^2 /n) / n-1) */
    if (regf[STAT_NUM_REG] == 0.0)
        msg(div_by_zero);
    else {
        tempyf = sqrt((regf[STAT_SUMY2_REG] - regf[STAT_SUMY_REG]*regf[STAT_SUMY_REG]/regf[STAT_NUM_REG])/(regf[STAT_NUM_REG] - 1));
        pushf(tempyf);
        tempxf = sqrt((regf[STAT_SUMX2_REG] - regf[STAT_SUMX_REG]*regf[STAT_SUMX_REG]/regf[STAT_NUM_REG])/(regf[STAT_NUM_REG] - 1));
        pushf(tempxf);
        dispnums();
        msg("Std Dev of X's & Y's");
    }
    return 0;
}

int
exec_perm() {
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    save_x();
    tempxf = popf();
    tempyf = popf();
    if (tempxf < 0 || tempyf < 0 || tempyf < tempxf) {
        msg("Bad values");
        pushf(tempyf);
        pushf(tempxf);
    } else {
        char buf[80];
        double n, r;

        n = floor(tempyf+.5);
        r = floor(tempxf+.5);
        pushf(fact(n, n - r));  
        dispnums();
        sprintf(buf, "Permutations (%.0f, %.0f)", r, n);
        msg(buf);
    }
    return 0;
}

int
exec_comb() {
    double      tempxf, tempyf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    save_x();
    tempxf = popf();
    tempyf = popf();
    if (tempxf < 0 || tempyf < 0 || tempyf < tempxf) {
        msg("Bad values");
        pushf(tempyf);
        pushf(tempxf);
    } else {
        double n, r;
        char buf[80];

        n = floor(tempyf+.5);
        r = floor(tempxf+.5);
        if (n-r > r)
            pushf(tempxf = fact(n, n-r) / fact(r, 1));
        else
            pushf(tempxf = fact(n, r) / fact(n-r, 1));
        dispnums();
        sprintf(buf, "Combinations (%.0f, %.0f)", r, n);
        msg(buf);
    }
    return 0;
}

int
exec_lr() {
    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    if (isInverted()) {
        char buf[80];

        sprintf(buf, "Linear Regression formula is: Y= %f*X + %f",
                regf[STAT_SLOPE], regf[STAT_Y_INT]);
        msg(buf);
    } else {
        if (regf[STAT_NUM_REG] < 2)
            msg("Not enough data points");
        else {
            double detx, dety, a, b, r2;

            detx = regf[STAT_SUMX2_REG]*regf[STAT_NUM_REG] - 
                regf[STAT_SUMX_REG]*regf[STAT_SUMX_REG];
            dety = regf[STAT_SUMY2_REG]*regf[STAT_NUM_REG] - 
                regf[STAT_SUMY_REG]*regf[STAT_SUMY_REG];
            a = (regf[STAT_SUMXY_REG]*regf[STAT_NUM_REG] - 
                 regf[STAT_SUMX_REG]*regf[STAT_SUMY_REG])/detx;
            b = (regf[STAT_SUMX2_REG]*regf[STAT_SUMY_REG] - 
                 regf[STAT_SUMX_REG]*regf[STAT_SUMXY_REG])/detx;
            r2 = a*a*detx/dety;
            regf[STAT_SLOPE] = a;
            regf[STAT_Y_INT] = b;
            regf[STAT_R2] = b;
            pushf(r2);
            pushf(b);
            pushf(a);
            dispnums();
            msg("Slope (X), Y-intersect (Y) & correlation (Z) calculated");
        }
    }
    return 0;
}
                
int
exec_calcy() {
    double      tempxf, tempyf;

    last_was_fin_key = 0;
    if (mode == PROG)
        return 0;
    if (isInverted()) {
        tempyf = popf();
        tempxf = (tempyf -  regf[STAT_Y_INT]) / regf[STAT_SLOPE];
        msg("X calculated from Y");
    } else {
        tempxf = popf();
        tempyf = regf[STAT_SLOPE] * tempxf + regf[STAT_Y_INT];
        msg("Y calculated from X");
    } 
    pushf(tempyf);
    pushf(tempxf);
    dispnums();
    return 0;
}

static int dayFunction(double f) {
    long y, m, d, x, z;
        
    f += 0.00005; /* round up */
    y = f;
    f = (f - y) * 100;
    m = f;
    f = (f - m) * 100;
    d = f;
    if (m <= 2) {
        x = 0;
        z = y - 1;
    } else {
        x = (long) (0.4*m + 2.3);
        z = y;
    }
    return 365*y + 31 *(m - 1) + d + (int)(z/4) - x;
}

static int dayFunction30(double f, int i) {
    static int was30or31;
    int y, m, d, a1;
        
    f += 0.00005; /* round up */
    y = f;
    f = (f - y) * 100;
    m = f;
    f = (f - m) * 100;
    d = f;
        
    if (i == 1) {
        if (d == 31) {
            a1 = 30;
        } else {
            a1 = d;
        }
        if (d == 30 || d == 31) {
            was30or31 = 1;
        } else {
            was30or31 = 0;
        }
    } else {
        if (d == 31) {
            if (was30or31) {
                a1 = 30;
            } else {
                a1 = d;
            }
        } else {
            a1 = d;
        }
    }
    return 360*y + 30*m + a1;
}

/* static void financial(COMMAND c) { */
    /*
     * Financial function register allocation ...
     *
     * 5 n
     * 6 interest
     * 7 present value
     * 8 payment
     * 9 future value
     *
     * Formulae are :
     *
     * 0 = PVAL + (1 + I.S) PMT (1 - (1+I)**-N ) / I + FVAL * (1+I)**-N

     New Formula from psion version:
     y=Fval + Pval*(1 + i)**n + (1 + s*i)*pmt*((1 + i)**n - 1)/i
     y'=pmt*(1 + (1 + i)^(n-1) * n * (pval* i^2 / pmt - (1+i)/n + i + s*i^2)) / i^2
     *
     * where:
     *
     * N    = number of periods
     * PVAL = present value
     * I    = interest rate (as a fraction - user sees a percent)
     * PMT  = payment
     * FVAL = future value
     * S    = payment mode factor. 0 for payment at end of period, 1 for start.
     */

static double
calc_fval(double pval, 
          double n, 
          double pmt, 
          double interest) {

    double tempxf, tempyf;

    if (fabs(interest) < 1.0e-13) {
        tempxf = -(pval + (n + finPayAt0) * pmt);
    } else {
        tempyf = pow(1.0 + interest, n);
#if 0
        if (errno) {
            printf("Error in calc_fval: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
            exit(1);
        }
#endif
        tempxf = -(pval*tempyf + (1 + finPayAt0*interest)*pmt*(tempyf - 1.0)/interest);
    }
    return tempxf;
}

static double
calc_pval(double fval, 
          double n, 
          double pmt, 
          double interest) {

    double tempxf, tempyf;

    if (fabs(interest) < 1.0e-13) {
        tempxf = -(fval + (n + finPayAt0)*pmt);
    } else {
        tempyf = pow(1.0 + interest, -n);
#if 0
        if (errno) {
            printf("Error in calc_pval: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
            exit(1);
        }
#endif
        tempxf = -(fval*tempyf + (1 + finPayAt0*interest)*pmt*(1.0 - tempyf)/interest);
    }
    return tempxf;
}

static double
calc_n(double fval, 
       double pval, 
       double pmt, 
       double interest) {

    double tempxf, tempyf, den;

    tempxf = 0.0;
    if (fabs(interest) < 1.0e-13) {
        if (fabs(pmt) < 1.0e-13) {
            msg(div_by_zero);
        } else {
            tempxf = -(fval + pval)/pmt - finPayAt0;
        }
    } else {
        tempyf = 1.0 + interest*finPayAt0;
        den = pval + tempyf*pmt/interest;
        if (fabs(pmt) < 1.0e-13) {
            msg(div_by_zero);
        } else {
            tempxf = log((tempyf*pmt/interest - fval)/den)/log(1.0 + interest);
        }
    }
    return tempxf;
}

static double
calc_pmt(double fval, 
         double pval, 
         double n, 
         double interest) {

    double tempxf, tempyf;

    if (fabs(interest) < 1.0e-13) {
        tempxf = -(fval + pval)/(n + finPayAt0);
    } else {
        tempyf = pow(1.0 + interest, n);
#if 0
        if (errno) {
            printf("Error in calc_pmt: pow(): interest=%.4f%% n=%.2f\n", interest*100.0, n);
            exit(1);
        }
#endif
        tempxf = -interest*(fval + pval*tempyf)/((1.0 + interest*finPayAt0)*(tempyf - 1.0));
    }
    return tempxf;
}

int
exec_pval() {
    double      tempxf;
    double      interest, n, pmt, fval;

    if (mode == PROG)
        return 0;

    if (isInverted()) {
        pushf(regf[FIN_PVAL_REG]);
        dispnums();
        msg("Present value recalled");
    } else if (!last_was_fin_key) {
        save_x();
        tempxf = popf();
        pushf(tempxf);
        regf[FIN_PVAL_REG] = tempxf;
        dispreg(FIN_PVAL_REG);
        dispnums();
        last_was_fin_key = TRUE;
        msg("Present value entered");
    } else {
        n = regf[FIN_NUM_REG];
        interest = regf[FIN_INT_REG] / 100.0;
        if (n <= 0.0) {
            msg("# payments must be > 0");
            return 0;
        }
        if (interest < 0.0) {
            msg("Interest must be >= 0");
            return 0;
        }
        pmt = regf[FIN_PMT_REG];
        fval = regf[FIN_FVAL_REG];
        tempxf = calc_pval(fval, n, pmt, interest);
        pushf(tempxf);
        regf[FIN_PVAL_REG] = tempxf;
        dispreg(FIN_PVAL_REG);
        dispnums();
        msg("Present value computed");
    }
    return 0;
}
        
int
exec_fval() {
    double      tempxf;
    double      interest, n, pval, pmt;

    if (mode == PROG)
        return 0;

    if (isInverted()) {
        pushf(regf[FIN_FVAL_REG]);
        dispnums();
        msg("Future value recalled");
    } else if (!last_was_fin_key) {
        save_x();
        tempxf = popf();
        pushf(tempxf);
        regf[FIN_FVAL_REG] = tempxf;
        dispreg(FIN_FVAL_REG);
        dispnums();
        last_was_fin_key = TRUE;
        msg("Final value entered");
    } else {
        n = regf[FIN_NUM_REG];
        interest = regf[FIN_INT_REG] / 100.0;
        if (n <= 0.0) {
            msg("# payments must be > 0");
            return 0;
        }
        if (interest < 0.0) {
            msg("Interest must be >= 0");
            return 0;
        }
        pval = regf[FIN_PVAL_REG];
        pmt = regf[FIN_PMT_REG];
        tempxf = calc_fval(pval, n, pmt, interest);
        pushf(tempxf);
        regf[FIN_FVAL_REG] = tempxf;
        dispreg(FIN_FVAL_REG);
        dispnums();
        msg("Final value calculated");
    }
    return 0;
}
        
int
exec_pmt() {
    double      tempxf;
    double      interest, n, pval, fval;

    if (mode == PROG)
        return 0;

    if (isInverted()) {
        pushf(regf[FIN_PMT_REG]);
        dispnums();
        msg("Payment recalled");
    } else if (!last_was_fin_key) {
        save_x();
        tempxf = popf();
        pushf(tempxf);
        regf[FIN_PMT_REG] = tempxf;
        dispreg(FIN_PMT_REG);
        dispnums();
        last_was_fin_key = TRUE;
        msg("Payment entered");
    } else {
        n = regf[FIN_NUM_REG];
        if (n <= 0.0) {
            msg("# payments must be > 0");
            return 0;
        }
        interest = regf[FIN_INT_REG] / 100.0;
        if (interest < 0.0) {
            msg("Interest must be >= 0");
            return 0;
        }
        pval = regf[FIN_PVAL_REG];
        fval = regf[FIN_FVAL_REG];
        tempxf = calc_pmt(fval, pval, n, interest);
        pushf(tempxf);
        regf[FIN_PMT_REG] = tempxf;
        dispreg(FIN_PMT_REG);
        dispnums();
        msg("Payment calculated");
    }
    return 0;
}

static double 
newton_raphson_interest(double fval, 
                        double pval, 
                        double n, 
                        double pmt, 
                        double guess,
                        double max_guess,
                        int *converged) {

    double epsilon, last_epsilon, fy, dy, tempyf, intst;
    int count, epsilon_increasing;

    count = epsilon_increasing = 0;
    last_epsilon = epsilon = 1000.0;

    intst = 0.0;
    if (guess == 0.0)
        guess = 0.0001;

    if (fabs(fval - pval - n * pmt) > 1.0e-13) {
        while ((count < 10000) && (fabs(epsilon) > 1.0e-15)) {
            tempyf = pow(1.0 + guess, n);
#if 0
            if (errno) {
                printf("Error in newton_raphson_interest: pow(): guess=%.4f%% n=%.2f\n", guess*100.0, n);
                exit(1);
            }
#endif
            fy = fval + pval*tempyf + (1.0 + finPayAt0*guess)*pmt*(tempyf - 1.0)/guess;

            tempyf = pow(1.0 + guess, n-1);
#if 0
            if (errno) {
                printf("Error in newton_raphson_interest: dy: pow(): guess=%.4f%% n=%.2f\n", guess*100.0, n);
                exit(1);
            }
#endif
            dy = (((pval + pmt*(double)finPayAt0)*n*guess*guess + (n - 1.0)*guess*pmt - pmt)*tempyf + pmt)/(guess*guess);

            epsilon=fy/dy;
            guess -= epsilon;
            if (fabs(epsilon) > fabs(last_epsilon))
                epsilon_increasing++;
            else
                epsilon_increasing = 0;
            if (epsilon_increasing > 5) { /* give up */
                *converged = 0;
                return intst;
            }

            if (guess > max_guess) { /* give up */
                converged = 0;
                return intst;
            }

            last_epsilon = epsilon;
            count++;
        }
        intst = guess;
    }
    *converged = 1;
    return intst;
}

static double
init_guess(double fval, double pval, double n, double pmt) {
    double guess;

    if (fabs(pval) < 1.0e-11)
        /* guess = -2.0*(fval + pmt*n)/(pmt*(n + 1.0)*n); */
        guess = 2.0*(pow(-fval/(n*pmt),1.0/n) - 1.0);
    else
        guess = pow(-(fval + pmt*n)/pval, 1.0/n) - 1.0;
    return(guess);
}

/*
 * this is a financial calculation after all, so it is fair to assume
 * that interest is 0-1000% and that either pval or fval is
 * non-zero. Also n>=1.
 *
 * The initial guess must not blow up pow(1+guess,n) so initial guess < pow(L,1/n)-1
 */
static double
calc_intst(double fval, 
           double pval, 
           double n, 
           double pmt,
           int *converged) {

    int count = 0, watch_pval;
    double guess, lower_guess, upper_guess, error, intst, max_guess, newguess;
    double calculated_val, good_enough_guess, val;

#define MAX_GUESSES 50
#define MAX_INTEREST_RATE 1.0e10 /* financial calculation, right? */
  
    lower_guess = intst = 0.0;
    upper_guess = pow(DBL_MAX,1.0/n)-1.0;
        
    if (upper_guess > MAX_INTEREST_RATE)
        upper_guess = MAX_INTEREST_RATE;
    max_guess = upper_guess;
    
    if (fabs(pval) < 1.0e-11 && fabs(pmt) < 1.0e-11) {
        converged = 0;
        return intst;
    }

    /* keep an eye on pval if fval is zero: */
    watch_pval = (fabs(fval) < 1.0e-11);
    if (watch_pval && fabs(pval) < 1.0e-11) {
        *converged = 0;
        return intst;
    }

    val = watch_pval? pval: fval;
    good_enough_guess = fabs(val) * 0.05;

    /* guess high in the 'normal' case to avoid exploring the looney
         * rates. This first recursion is from Steve Steps:
         */
    guess = init_guess(fval, pval, n, pmt);
    if (fabs(pval) > 1.0e-11) { /* tends to converge if pval != 0 */
        int rounds = 20;
        double newguess;

        while (rounds--) {
            if (guess > max_guess) {
                break;
            } else {
                newguess = (1.0+guess*finPayAt0)*pmt*(1.0-pow(1.0+guess,n)) / (fval + pval*pow(1.0+guess,n));
            }
            if (newguess <= 0.0) { /* it failed */
                /* printf("Negative guess. Reverting to initial guess\n"); */
                guess = init_guess(fval, pval, n, pmt);
                break;
            }

            calculated_val = watch_pval? 
                calc_pval(fval, n, pmt, guess):
                calc_fval(pval, n, pmt, guess);
            error = fabs(calculated_val - val);
            if (error < good_enough_guess)
                break; /* good enough */
            guess = newguess;
        }
    }

    if (guess > max_guess) {
        /* printf("Guess too high (%f%%)\n", guess*100.0); */
        guess = init_guess(fval, pval, n, pmt);
        if (guess > max_guess)
            guess = max_guess / 4.0;
    }

    /* Try and improve guess with a binary chop */
    while (count < MAX_GUESSES) {
        calculated_val = watch_pval? 
            calc_pval(fval, n, pmt, guess):
            calc_fval(pval, n, pmt, guess);
        error = fabs(calculated_val - val);

        if (error < good_enough_guess) {
            intst = newton_raphson_interest(fval, pval, n, pmt, guess, max_guess, converged);

            if (!*converged) {
                *converged = 0;
                return intst;
            }

            calculated_val = watch_pval? 
                calc_pval(fval, n, pmt, intst):
                calc_fval(pval, n, pmt, intst);

            if (fabs(val - calculated_val) > (fabs(val) / 1000.0)) {
                *converged = 0;
                return intst;
            }
                                
            *converged = 1;
            return intst;

        }

        if (fabs(upper_guess - lower_guess) < 1.0e-15) {
            *converged = 0;
            break;
        }

        /* need a new guess ... */
        if (calculated_val < val)
            upper_guess = guess;
        else    
            lower_guess = guess;
        count++;
        newguess = (upper_guess + lower_guess) / 2.0;
        if (fabs(newguess - guess) < 0.000001)
            break;
        guess = newguess;
    }

    /* Binary chop failed, go back to initial guess in desperation */
    guess = init_guess(fval, pval, n, pmt);
    intst = newton_raphson_interest(fval, pval, n, pmt, guess, max_guess, converged);

    if (!*converged) {
        return intst;
    }

    calculated_val = watch_pval? 
        calc_pval(fval, n, pmt, intst):
        calc_fval(pval, n, pmt, intst);

    if (fabs(val - calculated_val) > (fabs(val) / 1000.0)) {
        *converged = 0;
        return intst;
    }

    *converged = 1;
    return intst;
}
        
int
exec_intst() {
    double      tempxf;
    double      n, pval, pmt, fval, intst;
    int converged;

    if (mode == PROG)
        return 0;

    if (isInverted()) {
        pushf(regf[FIN_INT_REG]);
        dispnums();
        msg("Interest recalled");
    } else if (!last_was_fin_key) {
        save_x();
        tempxf = popf();
        pushf(tempxf);
        if (tempxf < 0.0) {
            msg("Interest cannot be < 0");
            return 0;
        }
        regf[FIN_INT_REG] = tempxf;
        dispreg(FIN_INT_REG);
        dispnums();
        last_was_fin_key = TRUE;
        msg("Interest (%) entered");
    } else {
        n = regf[FIN_NUM_REG];
        if (n <= 0.0) {
            msg("# payments must be > 0");
            return 0;
        }
        pval = regf[FIN_PVAL_REG];
        pmt = regf[FIN_PMT_REG];
        fval = regf[FIN_FVAL_REG];
        intst = 0.0;
        if (fabs(fval + pval + (n + finPayAt0)*pmt) == 0.0) {
            tempxf = 0.0;
            converged = 1;
        } else {
            /* use Newton-Raphson iteration ...
               if we are seeking roots of f(x)=0 then
               x_n+1 = x_n - f(x_n) / f'(x_n)

               The tricky part is the initial guess. The relationship
               is very unstable in 'i' and you have to get quite close
               to have a chance of converging.

            */

            intst = calc_intst(fval, pval, n, pmt, &converged);
        }
        if (converged) {
            tempxf = intst * 100.0; /* Convert to percent */
            pushf(tempxf);
            regf[FIN_INT_REG] = tempxf;
            dispreg(FIN_INT_REG);
            dispnums();
            msg("Interest (%) computed");
        } else
            msg("Calculation does not converge!\007");
    }
    return 0;
}
        
int
exec_nfin() {
    double      tempxf;
    double      interest, pval, pmt, fval;

    if (mode == PROG)
        return 0;

    if (isInverted()) {
        pushf(regf[FIN_NUM_REG]);
        dispnums();
        msg("# payments recalled");
    } else if (!last_was_fin_key) {
        save_x();
        tempxf = popf();
        pushf(tempxf);
        if (tempxf <= 0.0) {
            msg("# payments must be > 0");
            return 0;
        }
        regf[FIN_NUM_REG] = tempxf;
        dispreg(FIN_NUM_REG);
        dispnums();
        last_was_fin_key = TRUE;
        msg("# payments entered");
    } else {
        interest = regf[FIN_INT_REG] / 100.0;
        if (interest < 0.0) {
            msg("Interest must be >= 0");
            return 0;
        }
        pval = regf[FIN_PVAL_REG];
        pmt = regf[FIN_PMT_REG];
        fval = regf[FIN_FVAL_REG];
        tempxf = calc_n(fval, pval, pmt, interest);
        if (tempxf > 0) {
            pushf(tempxf);
            regf[FIN_NUM_REG] = tempxf;
            dispreg(FIN_NUM_REG);
            dispnums();
            msg("# payments computed");
        }
    }
    return 0;
}

int
exec_fclr() {
    int c;

    isInverted();
    if (mode == PROG)
        return 0;

    c = dialog("Clear all finance registers?" DIALOG_LETTERS);
    if (toupper(c) == 'Y') {
        int i;
        for (i=5; i< 10; i++)
            regf[i] = 0;
        msg("Fin registers cleared");
    }
    return 0;
}

int
exec_begin() {
    isInverted();

    if (mode == PROG)
        return 0;

    if (finPayAt0) {
        finPayAt0 = 0;
        msg("Annuity in arrears");
    } else {
        finPayAt0 = 1;
        msg("Annuity in advance");
    }
    return 0;
}

int
exec_dys() {
    double      tempxf, tempyf;
    if (mode == PROG)
        return 0;

    save_x();
    tempxf = popf();
    tempyf = popf();
    if (isInverted()) {
        tempyf = dayFunction30(tempxf,1);
        tempxf = dayFunction30(tempyf,2) - tempyf;
    } else {
        tempxf = dayFunction(tempyf) - dayFunction(tempxf);
    }
    msg("days between two dates calculated");
    if (decplaces < 4)
        decplaces = 4;
    if (floatmode != FIX)
        exec_fix();
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_tdy() {
    double      tempxf;
    struct tm *tmbuf;
    time_t t;

    isInverted();
    if (mode == PROG)
        return 0;
    save_x();

    t = time(NULL);
    tmbuf = localtime(&t);
    tempxf = tmbuf->tm_year + 1900;
    tempxf += (tmbuf->tm_mon + 1) / 100.0;
    tempxf += tmbuf->tm_mday / 10000.0;
    pushf(tempxf);
    if (decplaces != 4)
        decplaces = 4;
    if (floatmode != FIX)
        exec_fix();
    dispnums();
    msg("Today's date");
    return 0;
}

int
exec_times12() {
    double      tempxf;

    if (isInverted())
        return exec_divide12();

    if (mode == PROG)
        return 0;
    save_x();

    tempxf = popf();
    tempxf *= 12.0;
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_divide12() {
    double      tempxf;

    if (isInverted())
        return exec_times12();

    if (mode == PROG)
        return 0;

    save_x();
    tempxf = popf();
    tempxf /= 12.0;
    pushf(tempxf);
    dispnums();
    return 0;
}


static void process_digit(COMMAND c) {
    switch (c) {
    case BACKSPACE:
    case '0':
    case '1':
        echo_char(c);
        break;
        
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
        if (mode == PROG && intmode == BIN)
            put_a_char(BELL);
        else
            echo_char(c);
        break;
        
    case '8':
    case '9':
        if ((mode == PROG) && ((intmode == BIN) || (intmode == OCT)))
            put_a_char(BELL);
        else
            echo_char(c);
        break;
        
    case 'A':
    case 'B':
    case 'C':
    case 'D':
    case 'F':
        if (mode != PROG || intmode != HEX)
            put_a_char(BELL);
        else
            echo_char(c);
        break;
        
    case 'E':
        if (mode == PROG) {
            if (intmode == HEX) {
                echo_char(c);
            } else {
                put_a_char(BELL);
            }
        } else {
            if (strchr(inbuf, 'E') == NULL) {
                if (strlen(inbuf) == 0)
                    echo_char('1');
                echo_char(c);
            } else {
                msg("Can't put another E in the number!");
                put_a_char(BELL);
            }
        }
        break;
        
    case '.':
        if (mode == PROG) { /* ASCIIM never reaches here! */
            if (intmode == IP) {
                echo_char('.');
            } else {
                put_a_char(BELL);
            }
        } else if (entering && strchr(inbuf, '.'))
            put_a_char(BELL);
        else
            echo_char(c);
        break;
        
    default:
        put_a_char(BELL);
        break;
    }
}

/* trim all white space from buf (for units) */
void trim(char const *s) {
    char *d = (char *)s;
    while (*s) {
        if (!isspace(*s))
            *d++ = *s;
        s++;
    }
    *d = 0;
}

void convertX(char const *from, char const *to) {
#ifdef unix
    int fd[2], pid;
#endif

    if (mode == PROG) {
        msg("No conversions in PROG mode");
        return;
    }

    trim(from);
    trim(to);

    stop_entering();

#ifdef unix
    if (pipe(fd) < 0)
        msg("Can't open a pipe.");
    else {
        double oldX = xfReg;
        char *argv[5];
        char fromarg[80];

        if (fabs(oldX) < 0.0001)
            oldX = 1.0;

        sprintf(fromarg, "%f %s", oldX, from);
        argv[0] = "/usr/bin/units";
        argv[1] = "-o%.16g";
        argv[2] = fromarg;
        argv[3] = (char *)to;
        argv[4] = 0;
        if ((pid = vfork()) == -1) {
            msg("Fork failed.");
        } else if (pid == 0) { /* child - put stdout on pipe */
            close(fd[0]); /* child does not need to read from pipe */
            close(1); /* set stdout to the pipe */
            dup(fd[1]);
            close(fd[1]);
            execvp(argv[0], argv);
            _exit(1); /* abandon hope all ye who get here */
        } else { /* PARENT - read from pipe */
            FILE *p;
            close(fd[1]); /* parent does not write to pipe */
            if ((p = fdopen(fd[0], "r")) == NULL) {
                msg("Can't read from pipe");
            } else {
                double newX;
                int numRead = 0, status, bytesRead = 0;
                char inbuf[80];

                while (fgets(inbuf, 80, p) != NULL) {
                    bytesRead += strlen(inbuf);
                    if (!numRead)
                        numRead = sscanf(inbuf, " * %lf", &newX);
                }
                wait(&status);
                if (numRead == 1 && 
                    WIFEXITED(status) && 
                    WEXITSTATUS(status) == 0) {
                    double xf;

                    xf = popf();
                    pushf(newX);
                    if (fabs(xf) < 0.0001)
                        sprintf(inbuf, "Conversion factor %s to %s", from, to);
                    else
                        sprintf(inbuf, "Converted %s to %s", from, to);
                    msg(inbuf);
                    dispnums();
                } else if (bytesRead)
                    /* Note: if you are tempted to catch units's error
                       message be aware that as of version 1.55 it
                       does not use stderr! */
                    msg("Bad units");
                else
                    msg("Can't find the units program - please install it!");

                fclose(p); /* no need to close fd[1] too */
            }
        }
    }
#else
    msg("Conversions not supported here");
#endif
}

/* void storeOp(int op, int i) { */

static void
stoplus(int i) {
    if ((i >= 0) && (i < NUMREGS)) {
        char buf[80], *s;
        if (mode == PROG)
            reg[i] += xiReg;
        else
            regf[i] += xfReg;
        strcpy(buf, "Added to R");
        dispreg(i);
        s = buf + strlen(buf);
        sprintf(s, "%d", i);
        msg(buf);
    }
}

static void
stominus(int i) {

    if ((i >= 0) && (i < NUMREGS)) {
        char buf[80], *s;
        if (mode == PROG)
            reg[i] -= xiReg;
        else
            regf[i] -= xfReg;
        strcpy(buf, "Subtracted from R");
        dispreg(i);
        s = buf + strlen(buf);
        sprintf(s, "%d", i);
        msg(buf);
    }
}

static void
stotimes(int i) {
    if ((i >= 0) && (i < NUMREGS)) {
        char buf[80], *s;
        if (mode == PROG)
            reg[i] *= xiReg;
        else
            regf[i] *= xfReg;
        strcpy(buf, "Multiplied into R");
        dispreg(i);
        s = buf + strlen(buf);
        sprintf(s, "%d", i);
        msg(buf);
    }
}

static void
stodivide(int i) {
    if ((i >= 0) && (i < NUMREGS)) {
        char buf[80], *s;
        if (mode == PROG)
            reg[i] /= xiReg;
        else
            regf[i] /= xfReg;
        strcpy(buf, "Divided into R");
        dispreg(i);
        s = buf + strlen(buf);
        sprintf(s, "%d", i);
        msg(buf);
    }
}

int
exec_store() {
    int i, j;

    isInverted();
    last_was_fin_key = 0;
    stop_entering();
    i = store("Store in register [0-9] (or [+-*/] reg.)");
    j = i;
    if ((j=='+') || (j=='-') || (j=='*') || (j=='/')) {
        char buf[80];
        sprintf(buf,"Store %c in register [0-9]", j);
        i = store(buf);
        if (isdigit(i))
            i -= '0';
        else
            return 0;
    } else {
        j = 0;
        if (isdigit(i))
            i -= '0';
        else
            return 0;
    }
    stop_entering();

    if ((i >= 0) && (i < NUMREGS)) {
        char buf[80], *s;
        switch (j) {
        case '+': stoplus(i); break;
        case '-': stominus(i); break;
        case '*': stotimes(i); break;
        case '/': stodivide(i); break;
        case 0:
            if (mode == PROG)
                reg[i] = xiReg;
            else
                regf[i] = xfReg;
            strcpy(buf, "Stored in R");
            dispreg(i);
            s = buf + strlen(buf);
            sprintf(s, "%d", i);
            msg(buf);
            break;
        }
    }
    return 0;
}

int
exec_stoplus() {
    int i;

    isInverted();
    last_was_fin_key = 0;
    stop_entering();
    i = store("Store + in register [0-9]");
    if (isdigit(i))
        i -= '0';
    else
        return 0;
    stop_entering();

    stoplus(i);
    return 0;
}

int
exec_stominus() {
    int i, j;

    isInverted();
    last_was_fin_key = 0;
    stop_entering();
    i = store("Store in register [0-9] (or [+-*/] reg.)");
    j = i;
    if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
        i = store("Store in register [0-9]");
    else {
        j = 0;
        if (isdigit(i))
            i -= '0';
        else
            return 0;
    }
    stop_entering();
    stominus(i);
    return 0;
}

int
exec_stomultiply() {
    int i, j;

    isInverted();
    last_was_fin_key = 0;
    stop_entering();
    i = store("Store in register [0-9] (or [+-*/] reg.)");
    j = i;
    if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
        i = store("Store in register [0-9]");
    else {
        j = 0;
        if (isdigit(i))
            i -= '0';
        else
            return 0;
    }
    stop_entering();
    stotimes(i);
    return 0;
}

int
exec_stodivide() {
    int i, j;

    isInverted();
    last_was_fin_key = 0;
    stop_entering();
    i = store("Store in register [0-9] (or [+-*/] reg.)");
    j = i;
    if ((j=='+') || (j=='-') || (j=='*') || (j=='/'))
        i = store("Store in register [0-9]");
    else {
        j = 0;
        if (isdigit(i))
            i -= '0';
        else
            return 0;
    }
    stop_entering();
    stodivide(i);
    return 0;
}

int
exec_rolldown() {
    long tempx;
    double tempxf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = pop(); 
        tiReg = tempx;
    } else {
        tempxf = popf();
        tfReg = tempxf;
    }
    dispnums();
    return 0;
}

int
exec_clx() {
    long tempx;
    double tempxf;

    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG) {
        liReg = pop(); 
        tempx = 0;
        push(tempx); 
    } else {
        lfReg = popf();
        tempxf = 0.0;
        pushf(tempxf);
    }
    lift_needed = FALSE;
    dispnums();
    return 0;
}

/* Clear stack (INV = clear Registers) */
int
exec_clr() {
    int c;

    if (isInverted()) {
        c = dialog("Clear all registers?" DIALOG_LETTERS);
        if (toupper(c) == 'Y') {
            int i;
            for (i=0; i< NUMREGS; i++)
                if (mode == PROG)
                    reg[i] = 0;
                else
                    regf[i] = 0;
            msg("Registers cleared");
        }
    } else {
        last_was_fin_key = 0;
        if (mode == PROG) {
            xiReg = yiReg = ziReg = tiReg = liReg = 0;
        } else {
            xfReg = yfReg = zfReg = tfReg = lfReg = 0;
        }
        lift_needed = FALSE;
        dispnums();
    }
    return 0;
}

int
exec_toggle_deg() {
    isInverted();
    last_was_fin_key = 0;
    degree = !degree;
    print_deg();
    return 0;
}

int
exec_degree() {
    isInverted();
    last_was_fin_key = 0;
    degree = 1;
    print_deg();
    msg("Angles are in degrees");
    return 0;
}

int
exec_radian() {
    isInverted();
    last_was_fin_key = 0;
    degree = 0;
    print_deg();
    msg("Angles are in radians");
    return 0;
}

int
exec_e() {
    double tempxf;

    isInverted();
    last_was_fin_key = 0;
    tempxf = exp(1.0);
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_pi() {
    long tempx;
    double tempxf;

    isInverted();
    last_was_fin_key = 0;
    tempxf = pi;
    tempx = tempxf;
    pushf(tempxf);
    dispnums();
    return 0;
}

int
exec_enter() {
    long tempx;
    double tempxf;

    isInverted();
    last_was_fin_key = 0;

    if (mode == PROG) {
        tempx = pop(); 
        push(tempx); 
        push(tempx); 
    } else {
        tempxf = popf();
        pushf(tempxf);
        pushf(tempxf);
    }
    lift_needed = FALSE;
    dispnums();
    return 0;
}

#ifdef HAS_ALGEBRAIC_MODE

/* this is for algebraic method only - it all gets popped up one register */
static void
saveStack() {
    xfSave = xfReg;
    yfSave = xfReg;
    zfSave = yfReg;
    tfSave = zfReg;
    ufSave = tfReg;
    lfSave = lfReg;
    xSave = xiReg;
    ySave = xiReg;
    zSave = yiReg;
    tSave = ziReg;
    uSave = tiReg;
    lSave = liReg;
}

int
exec_rpn() {
    isInverted();
    last_was_fin_key = 0;
    algebraic_mode = 0;
    return 0;
}

int
exec_algebraic() {
    isInverted();
    last_was_fin_key = 0;
    algebraic_mode = 1;
    return 0;
}

int
exec_eval() {
    char expr[80];

    isInverted();
    last_was_fin_key = 0;
    if ((eval(last_eval, expr) == 0)) {
        stop_entering();
        strcpy(last_eval, expr);
        parserPointer = last_eval;
        saveStack();
        yyparse();
        dispnums();
    }
    return 0;
}

int
exec_equals() {
    isInverted();
    /* last_was_fin_key = 0; */
    lift_needed = FALSE;
    if (strlen(last_eval) == 0) {
        return 0;
    } else { /* add a missing closing brace: */
        int leftBraces = 0, rightBraces = 0;
        char *s = last_eval;
        while (s && *s && (s = strchr(s, '('))) {
            leftBraces++;
            s++;
        }
        s = last_eval;
        while (s && *s && (s = strchr(s, ')'))) {
            rightBraces++;
            s++;
        }
        while (rightBraces < leftBraces) {
            strcat(last_eval, ")");
            rightBraces++;
        }
    }
    
    parserPointer = last_eval;
    saveStack();
    yyparse();
    dispnums();
    return 0;
}

int
exec_leftbrace() {
    isInverted();
    last_was_fin_key = 0;
    lift_needed = FALSE;
    dispnums();
    return 0;
}

int
exec_rightbrace() {
    isInverted();
    last_was_fin_key = 0;
    lift_needed = FALSE;
    dispnums();
    return 0;
}
#endif

int
exec_lastx() {
    isInverted();
    last_was_fin_key = 0;
    if (mode == PROG)
        push(liReg); 
    else
        pushf(lfReg);
    dispnums();
    return 0;
}

int
exec_quit() {
    extern int is_resident;
    int c = QUIT;

    isInverted();
    last_was_fin_key = 0;
    if (is_resident) {
        c = QUIT;
    } else {

#ifndef HAVE_CONFIG_H /* gtk & EBOOKMAN */
        c = dialog("Really quit?" DIALOG_LETTERS);
        if (toupper(c) == 'Y') {
            c = QUIT;
            terminate_dcalc();
        } else
            c = 0;
        clear_msg();
#else
        terminate_dcalc();
#endif
    }
    return c;
}

/* For evaluation in algebraic expressions */
int
exec_rcl() {
    int i;
    i = (mode == PROG)? pop(): popf();
    (mode == PROG)? push(reg[i]): pushf(regf[i]);
    return 0;
}

int
exec_recall() {
    int i;

    isInverted();
    last_was_fin_key = 0;

    i = recall("Recall from register" DIALOG_NUMBERS);
        
    if (isdigit(i)) {
        i -= '0';
        if ((i >= 0) && (i < NUMREGS)) {
            char buf[80];

#ifdef HAS_ALGEBRAIC_MODE
            if (algebraic_mode) {
                sprintf(buf, "RCL(%d)", i);
                add_x(buf);
            } else
#endif
            {
                (mode == PROG)? push(reg[i]): pushf(regf[i]);
                dispnums();
            }
            sprintf(buf, "Recalled from R%d", i);
            msg(buf);
        } else
            msg("index out of bounds");
    }
    return 0;
}

int
exec_xny() {
    long tempx, tempy;
    double tempxf, tempyf;

    isInverted();
    stop_entering();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = pop(); 
        tempy = pop(); 
        push(tempx);   
        push(tempy);   
    } else {
        tempxf = popf();
        tempyf = popf();
        pushf(tempxf);  
        pushf(tempyf);  
    }
    dispnums();
    return 0;
}

int
exec_xnl() {
    long tempx;
    double tempxf;

    isInverted();
    stop_entering();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = xiReg;
        xiReg = liReg;
        liReg = tempx;
    } else {
        tempxf = xfReg;
        xfReg = lfReg;
        lfReg = tempxf;
    }
    dispnums();
    return 0;
}

int
exec_xnt() {
    long tempx;
    double tempxf;

    isInverted();
    stop_entering();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = xiReg;
        xiReg = tiReg;
        tiReg = tempx;
    } else {
        tempxf = xfReg;
        xfReg = tfReg;
        tfReg = tempxf;
    }
    dispnums();
    return 0;
}

int
exec_xnz() {
    long tempx;
    double tempxf;

    isInverted();
    stop_entering();
    last_was_fin_key = 0;
    if (mode == PROG) {
        tempx = xiReg;
        xiReg = ziReg;
        ziReg = tempx;
    } else {
        tempxf = xfReg;
        xfReg = zfReg;
        zfReg = tempxf;
    }
    dispnums();
    return 0;
}

int
exec_help() {
    isInverted();
    last_was_fin_key = 0;
    pop_up_help();
    return 0;
}

int
exec_reg() {
    isInverted();
    last_was_fin_key = 0;
    pop_up_reg();
    return 0;
}

int
exec_inv() {
    last_was_fin_key = 0;
    invert = !invert;
    print_inv();
    clear_msg();
    return 0;
}

int process(COMMAND c) {
    clear_msg();

#ifdef HAS_ALGEBRAIC_MODE
    if (algebraic_mode)
        if (c >= ' ' && c <= '~') {
            last_was_fin_key = 0;
            echo_char(c);
            return 0;
        } /* else a command */
#endif

    /* ASCII MODE - almost any character can be echo'ed */
    if ((c < MIN_COMMAND) && (mode == PROG && intmode == ASCIIM)) {
        last_was_fin_key = 0;
        echo_char(c); /* An ordinary character was typed */
        return 0;
    }
    
    if (c == INV) {
        exec_inv();
        return 0;
    }    
    
    if (c == BACKSPACE && isInverted()) {
        exec_clx();
        return 0;
    }

    if (((c >= '0') && (c <= '9')) ||
        ((c >= 'A') && (c <= 'F')) ||
        (c == BACKSPACE) ||
        (c == '.')) {
        process_digit(c);
        last_was_fin_key = 0;
        return 0;
    }
    
    switch (c) {
    case CHS:           exec_chs();             break;
    case PLUS:          exec_plus();            break;
    case MINUS:         exec_minus();           break;
    case DIVIDE:        exec_divide();          break;
    case MULT:          exec_mult();            break;
    case dAND:          exec_and();             break;
    case dOR:           exec_or();              break;
    case dXOR:          exec_xor();             break;
    case dNOT:          exec_not();             break;
    case MODULUS:       exec_modulus();         break;
    case PERCENT:       exec_percent();         break;
    case PERCENTCH:     exec_percentch();       break;
    case YTOX:          exec_ytox();            break;
    case SHIFTL:        exec_shiftl();          break;
    case SHIFTR:        exec_shiftr();          break;
    case SHIFTYL:       exec_shiftyl();         break;
    case SHIFTYR:       exec_shiftyr();         break;
    case PRIMEF:        exec_primef();          break;
    case RECI:          exec_reci();            break;
    case SQR:           exec_sqr();             break;
    case ROOT:          exec_root();            break;
    case CUBE:          exec_cube();            break;
    case CROOT:         exec_croot();           break;
    case PVAL:          exec_pval();            break;
    case FVAL:          exec_fval();            break;
    case PMT:           exec_pmt();             break;
    case INTST:         exec_intst();           break;
    case FCLR:          exec_fclr();            break;
    case NFIN:          exec_nfin();            break;
    case TIMES12:       exec_times12();         break;
    case DIVIDE12:      exec_divide12();        break;
    case BEGIN:         exec_begin();           break;
    case DYS:           exec_dys();             break;
    case TDY:           exec_tdy();             break;  
    case ASCIIM:        exec_asciim();          break;  
    case BIN:           exec_bin();             break;
    case IP:            exec_ip();              break;
    case OCT:           exec_oct();             break;
    case DEC:           exec_dec();             break;
    case HEX:           exec_hex();             break;
    case FIX:           exec_fix();             break;
    case ENG:           exec_eng();             break;
    case SCIFORMAT:     exec_sciformat();       break;
    case PLACES:        exec_places();          break;  
    case FIN:           exec_fin();             break;
    case STAT:          exec_stat();            break;
    case SCI:           exec_sci();             break;
    case PROG:          exec_prog();            break;
    case ROLLDOWN:      exec_rolldown();        break;
    case CLX:           exec_clx();             break;
    case CLR:           exec_clr();             break;
    case DEGREE:        exec_degree();          break;
    case RADIAN:        exec_radian();          break;
    case E:             exec_e();               break;
    case PI:            exec_pi();              break;
    case SIN:           exec_sin();             break;  
    case COS:           exec_cos();             break;
    case TAN:           exec_tan();             break;
    case SINH:          exec_sinh();            break;
    case COSH:          exec_cosh();            break;
    case TANH:          exec_tanh();            break;
    case LOGE:          exec_loge();            break;
    case LOG10:         exec_log10();           break;
    case FRC:           exec_frc();             break;
    case INT:           exec_int();             break;
    case ETOX:          exec_etox();            break;
    case HMS:           exec_hms();             break;
    case RTOP:          exec_rtop();            break;
    case DTOR:          exec_dtor();            break;
    case SUMR:          exec_sumr();            break;  
    case SUM0:          exec_sum0();            break;
    case NSTAT:         exec_nstat();           break;
    case MEAN:          exec_mean();            break;
    case MEANS:         exec_means();           break;
    case MEANY:         exec_meany();           break;
    case S_DEV:         exec_s_dev();           break;
    case SUM:           exec_sum();             break;
    case FACT:          exec_fact();            break;
    case COMB:          exec_comb();            break;
    case PERM:          exec_perm();            break;
    case LR:            exec_lr();              break;
    case CALCY:         exec_calcy();           break;
    case ENTER:         exec_enter();           break;
    case LASTX:         exec_lastx();           break;
    case QUIT:          c = exec_quit();        break;
    case RECALL:        exec_recall();          break;
    case STORE:         exec_store();           break;
    case STOPLUS:       exec_stoplus();         break;
    case STOMINUS:      exec_stominus();        break;
    case STOMULTIPLY:   exec_stomultiply();     break;
    case STODIVIDE:     exec_stodivide();       break;
    case XNY:           exec_xny();             break;
    case XNL:           exec_xnl();             break;
    case XNT:           exec_xnt();             break;  
    case XNZ:           exec_xnz();             break;
    case HELP:          exec_help();            break;
    case REGISTER:      exec_reg();             break;
#ifdef HAS_ALGEBRAIC_MODE
    case RPN:           exec_rpn();             break;
    case ALGEBRAIC:     exec_algebraic();       break;
    case EVAL:          exec_eval();            break;
    case EQUALS:        exec_equals();          break;
#endif
    case NOP:
        break;

    default:
        last_was_fin_key = 0;
        put_a_char(BELL);
        break;
    }
    return c;
}

#ifndef EBOOKMAN
static void getReg(char *b) {
    long l;
    int i;

    if (sscanf(b, "%d %ld", &i, &l))
        if (i >= 0 && i < NUMREGS)
            reg[i] = l;
}

static void getRegf(char *b) {
    double l;
    int i;

    if (sscanf(b, "%d %lg", &i, &l))
        if (i >= 0 && i < NUMREGS)
            regf[i] = l;
}

static void getMode(char *b) {
    mode = 
        (strcmp(b, "sci")==0)  ? SCI:
        (strcmp(b, "fin")==0)  ? FIN:
        (strcmp(b, "stat")==0) ? STAT:
        (strcmp(b, "prog")==0) ? PROG: SCI;
}

static void getIntmode(char *b) {
    intmode = 
        strcmp(b, "asci")==0? ASCIIM:
        strcmp(b, "ip")==0? IP:
        strcmp(b, "bin")==0? BIN:
        strcmp(b, "oct")==0? OCT:
        strcmp(b, "dec")==0? DEC:
        strcmp(b, "hex")==0? HEX: DEC;
}

static void getFloatmode(char *b) {
    floatmode = 
        strcmp(b, "FIX")==0? FIX:
        strcmp(b, "ENG")==0? ENG: 
        strcmp(b, "SCI")==0? SCI: FIX;
}
#endif  

static void readSettings() {
#ifdef EBOOKMAN
    readGuiSettings(NULL);
    return;
#else
    FILE *f = NULL;
    char *homeDir = NULL;
    char fileName[200], buf[80];

    homeDir = getenv("HOME");
    if (homeDir) {
        strcpy(fileName, homeDir);
        if (fileName[strlen(fileName) - 1] != '/')
            strcat(fileName, "/");
        strcat(fileName, ".dcalcrc");
        if ((f = fopen(fileName, "r")) != NULL) {
            while (fgets(buf, 80, f) != NULL) {
                char *b = buf, *e;

                if (*b && buf[strlen(buf) - 1] == '\n')
                    buf[strlen(buf) - 1] = 0;

                if (*b == '#')
                    continue;

                while (!isspace(*b))
                    b++;
                *b++ = 0;
                if (strcmp(buf, "x") == 0) xiReg = atol(b);
                if (strcmp(buf, "y") == 0) yiReg = atol(b);
                if (strcmp(buf, "z") == 0) ziReg = atol(b);
                if (strcmp(buf, "t") == 0) tiReg = atol(b);
                if (strcmp(buf, "l") == 0) liReg = atol(b);
                if (strcmp(buf, "xf") == 0) xfReg = atof(b);
                if (strcmp(buf, "yf") == 0) yfReg = atof(b);
                if (strcmp(buf, "zf") == 0) zfReg = atof(b);
                if (strcmp(buf, "tf") == 0) tfReg = atof(b);
                if (strcmp(buf, "lf") == 0) lfReg = atof(b);
                if (strcmp(buf, "reg") == 0) getReg(b);
                if (strcmp(buf, "regf") == 0) getRegf(b);
                if (strcmp(buf, "angularMode") == 0) degree = atoi(b);
                if (strcmp(buf, "places") == 0) decplaces = atoi(b);
                if (strcmp(buf, "mode") == 0) getMode(b);
                if (strcmp(buf, "intmode") == 0) getIntmode(b);
                if (strcmp(buf, "floatmode") == 0) getFloatmode(b);
                if (strcmp(buf, "gui") == 0) readGuiSettings(b);
                if (strcmp(buf, "annuitymode") == 0) finPayAt0 = atol(b);
#ifdef HAS_ALGEBRAIC_MODE
                if (strcmp(buf, "algebraicmode") == 0) algebraic_mode = atol(b);
                if (strcmp(buf, "lasteval") == 0) {
                    e = ++b;
                    while (*e && *e != '"')
                        e++;
                    *e = 0;
                    strcpy(last_eval, b);
                }
#endif
            }
            fclose(f);
        }
    }
#endif
}

void saveSettings(void) {
#ifdef EBOOKMAN
    stop_entering();
    saveGuiSettings(NULL);
    return;
#else
    FILE *f = NULL;
    char *homeDir = NULL;
    char fileName[200], buf[80];
    char lFormat[] = "%s %.25lg\n";
    char dFormat[] = "%s %ld\n";
    char iFormat[] = "%s %d\n";
    int i;

    stop_entering();
    homeDir = getenv("HOME");
    if (homeDir) {
        strcpy(fileName, homeDir);
        if (fileName[strlen(fileName) - 1] != '/')
            strcat(fileName, "/");
        strcat(fileName, ".dcalcrc");
        if ((f = fopen(fileName, "w")) != NULL) {
            fprintf(f, "# %s\n", "DCALC Version " VERSION " - by Bob Hepple");
            fprintf(f, "# don't edit this file - dcalc will overwrite it\n");
            fprintf(f, dFormat, "x", xiReg);
            fprintf(f, dFormat, "y", yiReg);
            fprintf(f, dFormat, "z", ziReg);
            fprintf(f, dFormat, "t", tiReg);
            fprintf(f, dFormat, "l", liReg);
            fprintf(f, lFormat, "xf", xfReg);
            fprintf(f, lFormat, "yf", yfReg);
            fprintf(f, lFormat, "zf", zfReg);
            fprintf(f, lFormat, "tf", tfReg);
            fprintf(f, lFormat, "lf", lfReg);
            for (i = 0; i < NUMREGS; i++) {
                sprintf(buf, "reg %d", i);
                fprintf(f, dFormat, buf, reg[i]);
            }
            for (i = 0; i < NUMREGS; i++) {
                sprintf(buf, "regf %d", i);
                fprintf(f, lFormat, buf, regf[i]);
            }
            fprintf(f, iFormat, "angularMode", degree);
            fprintf(f, iFormat, "places", decplaces);
            fprintf(f, "mode %s\n", 
                    mode==SCI? "sci":
                    mode==FIN? "fin":
                    mode==STAT? "stat":
                    mode==PROG? "prog":"sci");
            fprintf(f, "intmode %s\n", 
                    intmode==ASCIIM? "asci":
                    intmode==IP? "ip":
                    intmode==BIN? "bin":
                    intmode==OCT? "oct":
                    intmode==DEC? "dec":
                    intmode==HEX? "hex": "dec");
            fprintf(f, "floatmode %s\n",
                    floatmode==FIX? "FIX": "ENG");
            saveGuiSettings(f);
            /* Rev 2.7 on: */
            fprintf(f, "annuitymode %d\n", finPayAt0? 1: 0);
            /* Rev 2.8 on: */
#ifdef HAS_ALGEBRAIC_MODE
            fprintf(f, "algebraicmode %d\n", algebraic_mode? 1: 0);
            fprintf(f, "lasteval \"%s\"\n", last_eval);
#endif
            fclose(f);
        }
    }
#endif
}

void initialise(void) {
    int i;
    
    if (!was_initialised) {
        was_initialised = 1;
        os_init();
        
        xiReg = yiReg = ziReg = tiReg = liReg = 0;
        xfReg = yfReg = zfReg = tfReg = lfReg = 0.0;
        decplaces = 2;
#if EBOOKMAN
        mode = FIN;
#else
        mode = SCI;
#endif
        intmode = DEC;
        floatmode = FIX;
        entering = FALSE;
        lift_needed = TRUE;
        invert = FALSE;
        degree = TRUE;
        last_was_fin_key = FALSE;
        inptr = inbuf;
        *inptr = ASCIIZ;
        max_digits = 20;
        pi = 4.0 * atan(1.0);
        d_to_r = pi / 180.0;
        r_to_d = 180.0 / pi;
        finPayAt0 = 0;
#ifdef HAS_ALGEBRAIC_MODE
        last_eval[0] = 0;
#endif

        for (i = 0; i < NUMREGS; i++) {
            reg[i] = 0;
            regf[i] = 0.0;
        }

        readSettings();
        if ((mode == PROG) && (intmode == ASCIIM))
            os_raw_mode(1);
    }
}

extern void terminate_dcalc() {
    saveSettings();
    os_term();
}

/* For emacs: */

/* Local Variables: */
/* eval:(setq tab-width 8) */
/* End: */
